package com.demo.webapp.views.freemarker;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;

import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.views.freemarker.FreemarkerManager;
import org.apache.struts2.views.freemarker.StrutsClassTemplateLoader;

import com.demo.common.ClassLoaderUtils;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import com.opensymphony.xwork2.util.fs.DefaultFileManager;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;

import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.StringTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.cache.WebappTemplateLoader;
import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration;
import freemarker.template.TemplateException;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModelException;

/**
 * FreemarkerManage类的扩展类
 * 1.针对通过freemarkerStatic.properties配置文件,
 * 使Freemarker可以直接访问指定的静态变量或方法。
 * 2.增加继承类处理自定义指令的方法
 *  由registerTemplateDirectiveModel方法
 * 
 * @author ryuu.kk
 *
 */
public class FreemarkerManagerEx extends FreemarkerManager {

	private static final Log Logger = LogFactory.getLog(FreemarkerManagerEx.class);
	
	/**ClassLoader**/
	private ClassLoader classLoader = ClassLoaderUtils.getDefaultClassLoader();
	
    private static final Logger LOG = LoggerFactory.getLogger(FreemarkerManagerEx.class);
    
    private FileManager fileManager = new DefaultFileManager();
	/**
	 * 取得Configuration对象
	 * @param servletContext ServletContext 
	 * @return Configuration
	 */
	protected Configuration createConfiguration(ServletContext servletContext) throws TemplateException {
		
		this.config =  super.createConfiguration(servletContext);
		
		//config.setSharedVariable("aa_bb", new UrlMethod());
		//config.setSharedVariable("aa.bb", new UrlMethod());
		try {
			// 该方法已删除,使用loadSettings方法读取静态配置文件,加载静态类
			// 注册静态配置(静态方法/静态变量)
			// registerTemplateStaticModel(config, servletContext);
			
			// 注册自定义指令
			registerTemplateDirectiveModel(config, servletContext);
		} catch (Exception e) {
			throw new TemplateException(e.getMessage(), e, null);
		}
		return config;
	}
	
	/**
	 * 注册指令模型
	 * @param config Configuration
	 * @param servletContext ServletContext
	 */
	protected void registerTemplateDirectiveModel(Configuration config, ServletContext servletContext) throws Exception {
		
	}
	/**
	 * 扩展TemplateLoader
	 * 增加String类型的读取器
	 * 使得可以直接将模板String传入到Template直接执行。
	 * 
	 */
	@Override
	protected TemplateLoader createTemplateLoader(ServletContext servletContext, String templatePath) {
		
		TemplateLoader templatePathLoader = null;

         try {
             if(templatePath!=null){
                 if (templatePath.startsWith("class://")) {
                     // substring(7) is intentional as we "reuse" the last slash
                     templatePathLoader = new ClassTemplateLoader(getClass(), templatePath.substring(7));
                 } else if (templatePath.startsWith("file://")) {
                     templatePathLoader = new FileTemplateLoader(new File(templatePath));
                 } 
                 /*
                  else if (templatePath.startsWith("string://")) {
     			  	 // 新增String类型的模板读取类,将String模板对象可以直接转换为Template执行
                	 // 命名规则为string://${name}/${content}
                	 String content = templatePath.substring(9);
                	 int split = content.indexOf("/"); 
                	 String name = content.substring(0, split - 1);
                	 String templateSource = content.substring(name.length() + 1);
                	 templatePathLoader = new StringTemplateLoader();
                	 // 分解模板名&模板内容
                	 ((StringTemplateLoader)templatePathLoader).putTemplate(name, templateSource);
                 }
                 */
             }
         } catch (IOException e) {
             LOG.error("Invalid template path specified: " + e.getMessage(), e);
         }
         // presume that most apps will require the class and webapp template loader
         // if people wish to
         // 增加String模板直接执行Loader：new StringTemplateLoader()
         return templatePathLoader != null ?
                 new MultiTemplateLoader(new TemplateLoader[]{
                         templatePathLoader,
                         new WebappTemplateLoader(servletContext),
                         new StrutsClassTemplateLoader(),
                         new StringTemplateLoader()
                 })
                 : new MultiTemplateLoader(new TemplateLoader[]{
                 new WebappTemplateLoader(servletContext),
                 new StrutsClassTemplateLoader()
         });
	}
	
	/**
	 * 注册静态变量
	 * @param config Configuration
	 * @param servletContext ServletContext
	 */
	@Override
	protected void loadSettings(ServletContext servletContext) {
		// 是否默认Settings
		super.loadSettings(servletContext);
		
		InputStream in = null;  
		try {
			// 使用静态类Settings
			// edit by ryuu.kk at 2012-09-07 Struts2从2.3.1迁移至2.3.4.1,版本相应处理
			//in = FileManager.loadFile("freemarkerStatic.properties", FreemarkerManager.class);
			// edit by ryuu.kk
			
			in  = fileManager.loadFile(ClassLoaderUtil.getResource("freemarkerStatic.properties", FreemarkerManager.class));

			if (in != null) {
				Properties p = new Properties();
				p.load(in);
				if (wrapper instanceof BeansWrapper) {
					TemplateHashModel staticModels = ((BeansWrapper) wrapper).getStaticModels();
					for (Object o : p.keySet()) {
						String name = (String) o;
						String value = (String) p.get(name);
						TemplateHashModel tempStatics = (TemplateHashModel) staticModels.get(value);
						config.setSharedVariable(name, tempStatics);
					}
				}
			}
		} catch (IOException e) {
			LOG.error("Error while loading freemarker settings from /freemarkerStatic.properties", e);
		
		} catch (TemplateModelException e) {
			LOG.error("Error while loading freemarker static object from /freemarkerStatic.properties", e);
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException io) {
					if (LOG.isWarnEnabled()) {
						LOG.warn("Unable to close input stream", io);
					}
				}
			}
		}
	}
	
	/**
	 * 创建对象
	 * @param clazz 对象类
	 * @param extraContext 参数
	 * @return Object
	 * @throws Exception 创建异常
	 */
	protected Object buildBean(Class<?> clazz, Map<?, ?> extraContext) throws Exception {
		if (Logger.isDebugEnabled()) {
			Logger.debug("create bean class " + clazz.getName());
		}
		return clazz.newInstance();
	}
	
    protected Class<?> getClassInstance(String className) throws ClassNotFoundException {
    	if (this.classLoader != null) {
    		return classLoader.loadClass(className);
    	}
    	return ClassLoaderUtils.loadClass(className, this.getClass());
    }
    
    @Inject
    public void setFileManager(FileManager fileManager) {
        this.fileManager = fileManager;
        super.setFileManager(fileManager);
    }

	public ClassLoader getClassLoader() {
		return classLoader;
	}

	public void setClassLoader(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}
}